
// ===============================================================================================================
// -*- C++ -*-
//
// Enemy.cpp - Enemy base class for the AI controlled hostile agents.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <Enemy.hpp>
#include <Renderer.hpp>

// =========================================================
// Hellknight Class Implementation
// =========================================================

const float Hellknight::MOVEMENT_SPEED = 0.2f;
const float Hellknight::TIME_TILL_RESPAWN = 15.0f;

Hellknight::Hellknight(const Vec3 & initialPos)
: Enemy(), position(initialPos), rAngle(90.0f * Math::DEG_TO_RAD), speedAcc(MOVEMENT_SPEED), deadAngle(0.0f), respawnTime(0.0f)
{
	// Load the model and animations:

	DoomMD5AnimPtr anim;
	model = DoomMD5Factory::CreateModelFromFile("Assets/Models/Monsters/Hellknight/Hellknight.md5mesh");

	if (!model)
	{
		throw std::runtime_error("Failed to load the Hellknight model.");
	}

	anim = DoomMD5Factory::CreateAnimFromFile("Assets/Models/Monsters/Hellknight/Walk.md5anim");

	if (!anim)
	{
		throw std::runtime_error("Failed to load a Hellknight anim !");
	}

	model->RegisterAnimation(anim.Get(), "Walk");

	anim = DoomMD5Factory::CreateAnimFromFile("Assets/Models/Monsters/Hellknight/IK_Pose.md5anim");

	if (!anim)
	{
		throw std::runtime_error("Failed to load a Hellknight anim !");
	}

	model->RegisterAnimation(anim.Get(), "IK_Pose");

	// Now, load the hellknight textures:

	int n = model->GetNumMeshes();

	while (n--)
	{
		DoomMD5Mesh * mesh = model->GetMesh(n);

		if (mesh->shader[0] != '\0')
		{
			std::string textureFile(mesh->shader);
			textureFile.append(".tga"); // FIXME: Currently there is only support for TGA images.
			mesh->textureObject = Renderer::Instance()->Create2DTextureFromFile(textureFile);
		}
	}
}

void Hellknight::SetAngle(float ang)
{
	rAngle = ang;
}

float Hellknight::GetAngle(void) const
{
	return (rAngle);
}

void Hellknight::SetPosition(const Vec3 & pos)
{
	position.x = pos.x;
	position.y = pos.y;
	position.z = pos.z;
}

const Vec3 & Hellknight::GetPosition(void) const
{
	return (position);
}

void Hellknight::Update(GameLevel * level, float playerAngle, float elapsedTime)
{
	Matrix4x4 T(false), R(false); // Don't set to identity

	switch (aiState)
	{
	case Enemy::AI_STATE_DEAD:
		{
			respawnTime += elapsedTime;

			if (respawnTime > TIME_TILL_RESPAWN)
			{
				// Change AI state:
				aiState = Enemy::AI_STATE_ALIVE;

				// Set a new random position:
				int max_x, max_z;
				level->GetExtents(max_x, max_z);
				level->GetRandomPos(position, (max_x - 1) * max_x, (max_z - 1) * max_z);

				// Reset time counter:
				respawnTime = 0.0f;
			}

			DoomMD5Anim * anim = model->GetAnimation("IK_Pose");
			model->Animate(anim, elapsedTime);

			T.Translate(position);
			R.RotateY(deadAngle);

			Matrix4x4 locRotation(false);
			locRotation.RotateZ(90.0f * Math::DEG_TO_RAD);

			R = R * locRotation;
			rAngle += playerAngle;

			break;
		}
	case Enemy::AI_STATE_ALIVE:
		{
			DoomMD5Anim * anim = model->GetAnimation("Walk");
			const int loopComplete = model->Animate(anim, elapsedTime);

			rAngle += playerAngle;
			speedAcc += MOVEMENT_SPEED;
			deadAngle = rAngle;

			// The Doom 3 models have a very odd walk mode where the model is actually translated forward
			// than moved back to is original position when the loop is complete, instead of walking without changin position, as the usual.
			// This is a big problem to me, so I made this workarround to compensate for the tranlation. Still the result
			// is not so good. I'll leave it the way it is for now... Got no time to fix that...

			if (loopComplete)
			{
				float s, c;
				Math::SineCosine(rAngle, s, c);

				position.x += (c * speedAcc);
				position.z -= (s * speedAcc);
				position.y = level->GetHeightAt(position.x, position.z);

				speedAcc = MOVEMENT_SPEED;
			}

			anim->Release(); // Release anim.

			T.Translate(position);
			R.RotateY(rAngle);

			break;
		}
	} // End switch (aiState)

	// Custom matrix precalculated for the Hellknight. This will adjust the model rotation and scale.
	static const Matrix4x4 monsterOffset(
	0.25f, 0.0f,  0.0f,  0.0f,
	0.0f,  0.0f,  0.25f, 0.0f,
	0.0f,  0.25f, 0.0f,  0.0f,
	0.0f,  0.0f,  0.0f,  1.0f);

	transform = (T * R * monsterOffset); // Set transform.
}